
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <assert.h>
#include <stdio.h>

#include "Completion.h"
#include "ErrorObject.h"
#include "HostContext.h"
#include "HostValue.h"
#include "List.h"
#include "InternalFunction.h"
#include "Null.h"
#include "Object.h"
#include "Scope.h"
#include "String.h"
#include "Value.h"
#include "ValueType.h"
#include "esiply.h"

// Ecma 15.11.4
// The Error prototype is itself an Error object (its [[Class]] is "Error")
Object* Error::prototype = new Object(Null::null, Object::prototype,
            new String("Error"));
String* Error::name = new String("Error");
Object* EvalError::prototype = new Object(Null::null, Error::prototype,
            new String("Error"));
String* EvalError::name = new String("EvalError");
Object* RangeError::prototype = new Object(Null::null, Error::prototype,
            new String("Error"));
String* RangeError::name = new String("RangeError");
Object* ReferenceError::prototype = new Object(Null::null, Error::prototype,
            new String("Error"));
String* ReferenceError::name = new String("ReferenceError");
Object* SyntaxError::prototype = new Object(Null::null, Error::prototype,
            new String("Error"));
String* SyntaxError::name = new String("SyntaxError");
Object* TypeError::prototype = new Object(Null::null, Error::prototype,
            new String("Error"));
String* TypeError::name = new String("TypeError");
Object* URIError::prototype = new Object(Null::null, Error::prototype,
            new String("Error"));
String* URIError::name = new String("URIError");

template <typename error_type>
Value* error_construct(List* args)
{
    // Ecma 15.11.2.1 
    // The [[prototype]] property of athe newly constructed object is
    // set to the original Error prototype object [[Class] is "Error"
    // If the arguments messages is not undefined, the message property
    // of the newly constructed object is set to toString(message)
    Object* result = new Object(Null::null, error_type::prototype,
                new String("Error"));
    if (!args->empty() && args->get(0)->getType() != VT_UNDEFINED)
    {
        result->put(new String("message"), args->get(0)->toString(), PA_EMPTY);
    }
    return result;
}

template <typename error_type>
Value* error_call(Value* this_obj, List* args)
{
    // Ecma 15.11.1
    // Same as constructor
    return error_construct<error_type>(args);
}

// native_error_init initializes the specific error type
template <typename error_type>
void native_error_init(Scope* scope)
{
    Function* err = new InternalFunction(error_call<error_type>,
            error_construct<error_type>, 1);

    err->put(new String("prototype"), error_type::prototype,
            PA_DONT_ENUM | PA_DONT_DELETE | PA_READ_ONLY);

    scope->get_variable()->put(error_type::name, err, PA_DONT_ENUM);
}


// error_init does all errors
void error_init(Scope* scope)
{
    // Ecma 15.11.2 The Error constructor, and friends
    native_error_init<Error>(scope);
    native_error_init<EvalError>(scope);
    native_error_init<RangeError>(scope);
    native_error_init<ReferenceError>(scope);
    native_error_init<SyntaxError>(scope);
    native_error_init<TypeError>(scope);
    native_error_init<URIError>(scope);

    HostContext hc(scope);
    std::string path = esiply_getLibraryPath() + "Error.js";
    hc.run_file(path);
}


Value* esiply_throw(ErrorType type, const char* str)
{
    fprintf(stderr, "Throwing: %s\n", str);
    //assert(false);

    // Create the message
    List* args = new List();
    args->push_back(new String(str));

    // Create the error object
    Value* err = Null::null;
    switch (type)
    {
        case ET_ERROR:
            err = error_construct<Error>(args);
            break;

        case ET_EVAL:
            err = error_construct<EvalError>(args);
            break;

        case ET_RANGE:
            err = error_construct<RangeError>(args);
            break;

        case ET_REFERENCE:
            err = error_construct<ReferenceError>(args);
            break;

        case ET_SYNTAX:
            err = error_construct<SyntaxError>(args);
            break;

        case ET_TYPE:
            err = error_construct<TypeError>(args);
            break;

        case ET_URI:
            err = error_construct<URIError>(args);
            break;

        default:
            assert(false);
    }
    return new Completion(CT_THROW, err, Completion::empty);
}

